package org.datacoder.geocoding.datastructures

import org.datacoder.geocoding.datastructures.geoshapes.*
import org.datacoder.geocoding.datastructures.intervals.*
import org.datacoder.geocoding.datastructures.points.*
import org.datacoder.geocoding.datastructures.rectangles.*
import org.datacoder.geocoding.jurisdiction.*

/**
 * Holds a set of four data structures ("finders") which can
 * perform various simple geometric queries, and populates
 * them with jurisdictions from the Census data.  The user can
 * control which four data structures are used with the
 * constructor.  The data structures fall into one of four types:
 *
 * IntersectingIntervalsFinders (HZ/VT)
 * These structures locate which horizontal (vertical) intervals
 * intersect a query vertical (horizontal) intervals.  These come
 * in two types, depending on how they are oriented.
 *
 * PointsWithinRectangleFinders
 * These structures locate which points fall within a query
 * rectangle.
 *
 * RectanglesContainingPointFinders
 * These structures locate which rectangles contain a query
 * point.
 *
 * The GeocodingService can query the EdelsbrunnerMaurerStructure
 * to answer various high-level queries.  This structure then
 * issues subqueries to its four data structures to answer the query.
 */
class EdelsbrunnerMaurerStructure {

    private IntersectingIntervalsFinder ihiFinder
    private IntersectingIntervalsFinder iviFinder
    private PointsWithinRectangleFinder pwrFinder
    private RectanglesContainingPointFinder rcpFinder

    /* INIT */
    public EdelsbrunnerMaurerStructure(
        Collection<Jurisdiction> jurisdictions,
        Class ihiFinderClass,
        Class iviFinderClass,
        Class pwrFinderClass,
        Class rcpFinderClass
    ){
        try{
            ihiFinder = (IntersectingIntervalsFinder)ihiFinderClass.getConstructors()[0].newInstance(jurisdictions, GeoShape.LON)
            iviFinder = (IntersectingIntervalsFinder)iviFinderClass.getConstructors()[0].newInstance(jurisdictions, GeoShape.LAT)
            pwrFinder = (PointsWithinRectangleFinder)pwrFinderClass.getConstructors()[0].newInstance(jurisdictions)
            rcpFinder = (RectanglesContainingPointFinder)rcpFinderClass.getConstructors()[0].newInstance(jurisdictions)
        }catch(Exception e){
            e.printStackTrace()
            System.exit(1)
        }
    }

    /* MAIN METHOD */
    public Set<GeoShape> findRectanglesIntersectingRectangle(GeoRectangle gr){
        Set<Jurisdiction> jurisdictions = new HashSet<Jurisdiction>()
        jurisdictions.addAll(findPointsWithinRectangle(gr))
        jurisdictions.addAll(findRectanglesContainingPoint(new GeoPoint(
                    lon:gr.lon1,
                    lat:gr.lat1
                )))
        jurisdictions.addAll(findIntersectingHzIntervals(new GeoVtInterval(
                    lon:gr.lon1,
                    lat1:gr.lat1,
                    lat2:gr.lat2
                )))
        jurisdictions.addAll(findIntersectingVtIntervals(new GeoHzInterval(
                    lon1:gr.lon1,
                    lon2:gr.lon2,
                    lat:gr.lat1
                )))
        return jurisdictions
    }

    /* SUB METHODS */
    public Set<Jurisdiction> findIntersectingHzIntervals(GeoVtInterval gvi){
        return ihiFinder.findIntersectingIntervals(gvi)
    }
    public Set<Jurisdiction> findIntersectingVtIntervals(GeoHzInterval ghi){
        return iviFinder.findIntersectingIntervals(ghi)
    }
    public Set<Jurisdiction> findPointsWithinRectangle(GeoRectangle gr){
        return pwrFinder.findPointsWithinRectangle(gr)
    }
    public Set<Jurisdiction> findRectanglesContainingPoint(GeoPoint gp){
        return rcpFinder.findRectanglesContainingPoint(gp)
    }


}

